/* vim: set sw=4 sts=4 et : */
#include <stdlib.h>
#include "remotecores_protocol.h"

static remotecores_error_t *remotecores_socket_write(remotecores_protocol_t *
                                                     p, const char *data,
                                                     apr_size_t length)
{
    apr_status_t rc;
    apr_size_t nbytes = length, o, i;

    o = 0;
    i = nbytes;

    while (i > 0) {
        nbytes = i;
        rc = apr_socket_send(p->sock, data + o, &nbytes);
        if (rc != APR_SUCCESS) {
            return remotecores_error_create(rc, "apr_socket_send");
        }
        o += nbytes;
        i -= nbytes;
    }

    return REMOTECORES_SUCCESS;
}

static remotecores_error_t *remotecores_socket_fill(remotecores_protocol_t *
                                                    p)
{
    apr_size_t len = REMOTECORES_CHUNK_SIZE;
    apr_status_t rv;

    rv = apr_socket_recv(p->sock, p->buffer, &len);
    if (rv != APR_SUCCESS) {
        return remotecores_error_create(rv, "apr_socket_recv");
    }

    if (len > 0) {
        p->start = 0;
        p->end = len;
    }

    return REMOTECORES_SUCCESS;
}

static remotecores_error_t *remotecores_socket_read(remotecores_protocol_t *
                                                    p, char *ptr,
                                                    apr_size_t len,
                                                    apr_size_t * read_length)
{
    remotecores_error_t *err;
    apr_size_t i = len;
    apr_size_t l;
    apr_size_t nRead = 0;

    while (i > 0) {
        if (p->end - p->start > 0) {
            if (p->start + i <= p->end) {
                l = i;
            }
            else {
                l = p->end - p->start;
            }

            memcpy(ptr + (len - i), p->buffer + p->start, l);

            p->start += l;
            i -= l;
            nRead += l;
        }
        else {
            err = remotecores_socket_fill(p);
            if (err) {
                return err;
            }
        }
    }

    if (read_length)
        *read_length = nRead;

    return REMOTECORES_SUCCESS;
}

static remotecores_error_t*
remotecores_socket_readto(remotecores_protocol_t *p, 
                          char *ptr,
                          apr_size_t len,
                          char delim,
                          apr_size_t *read_length)
{
    apr_int32_t i = 0;
    apr_int32_t l, c;

    while (i < len) {
        if (p->end - p->start > 0) {
            for (c = 0, l = p->start; l < p->end;) {
                c = ptr[i++] = p->buffer[l++];
                if (c == delim || i == len) {
                    break;
                }
            }

            p->start = l;

            if (c == delim) {
                break;
            }
        }
        else {
            remotecores_error_t *err = remotecores_socket_fill(p);
            if (err) {
                return err;
            }
        }
    }

    *read_length = i;

    return REMOTECORES_SUCCESS;
}

remotecores_error_t *
remotecores_protocol_create(remotecores_protocol_t **p, apr_socket_t *sock, 
                            apr_pool_t *pool)
{
    remotecores_protocol_t *proto;
    
    proto = apr_palloc(pool, sizeof(remotecores_protocol_t));
    proto->sock = sock;
    proto->buffer[0] = 0;
    proto->start = 0;
    proto->end = 0;

    *p = proto;

    return REMOTECORES_SUCCESS;
}

static remotecores_error_t *
remotecores_netstring_write(remotecores_protocol_t *p, const char *data,
                            apr_size_t length)
{
    char buf[32];
    int len;
    remotecores_error_t *err;

    len = snprintf(buf, sizeof(buf), "%" APR_SIZE_T_FMT ":", length);
    err = remotecores_socket_write(p, buf, len);
    if (err) {
        return err;
    }

    if (data && length > 0) {
        err = remotecores_socket_write(p, data, length);
        if (err) {
            return err;
        }
    }

    return remotecores_socket_write(p, ",", 1);
}

remotecores_error_t *remotecores_offset_write(remotecores_protocol_t * p,
                                              apr_off_t * d)
{
    char buf[32];
    int len = snprintf(buf, sizeof(buf), "%" APR_OFF_T_FMT ",", *d);
    return remotecores_socket_write(p, buf, len);
}

remotecores_error_t *remotecores_uint32_write(remotecores_protocol_t * p,
                                              apr_uint32_t * d)
{
    char buf[32];
    int len = snprintf(buf, sizeof(buf), "%u,", *d);
    return remotecores_socket_write(p, buf, len);
}

remotecores_error_t *remotecores_int32_write(remotecores_protocol_t * p,
                                             apr_int32_t * d)
{
    char buf[32];
    int len = snprintf(buf, sizeof(buf), "%i,", *d);
    return remotecores_socket_write(p, buf, len);
}

remotecores_error_t *remotecores_string_write(remotecores_protocol_t * p,
                                              const char *str)
{
    int len = 0;

    if (str) {
        len = strlen(str) + 1;
    }

    return remotecores_netstring_write(p, str, len);
}

/* Readers */
remotecores_error_t *remotecores_offset_read(remotecores_protocol_t * p,
                                             apr_off_t * d)
{
    remotecores_error_t *err;
    char buf[32];
    apr_size_t len;

    err = remotecores_socket_readto(p, buf, sizeof(buf), ',', &len);
    if (err) {
        return err;
    }
    buf[len] = 0;

    sscanf(buf, "%" APR_OFF_T_FMT, d);

    return REMOTECORES_SUCCESS;
}

static remotecores_error_t * 
remotecores_uint32_read_delim(remotecores_protocol_t * p, char delim,
                              apr_uint32_t * d)
{
    remotecores_error_t *err;
    char buf[32];
    apr_size_t len;

    err = remotecores_socket_readto(p, buf, sizeof(buf), delim, &len);
    if (err) {
        return err;
    }
    buf[len] = 0;

    sscanf(buf, "%u", d);

    return REMOTECORES_SUCCESS;
}


remotecores_error_t *remotecores_uint32_read(remotecores_protocol_t * p,
                                             apr_uint32_t * d)
{
    return remotecores_uint32_read_delim(p, ',', d);
}

remotecores_error_t *remotecores_int32_read(remotecores_protocol_t * p,
                                            apr_int32_t * d)
{
    remotecores_error_t *err;
    char buf[32];
    apr_size_t len;

    err = remotecores_socket_readto(p, buf, sizeof(buf), ',', &len);
    if (err) {
        return err;
    }
    buf[len] = 0;

    sscanf(buf, "%i", d);

    return REMOTECORES_SUCCESS;
}

remotecores_error_t *remotecores_string_read(remotecores_protocol_t * p,
                                             char **str)
{
    remotecores_error_t *err;
    apr_uint32_t len;

    err = remotecores_uint32_read_delim(p, ':', &len);
    if (err) {
        return err;
    }

    if (len == 0) {
        *str = NULL;
    }
    else {
        char tmp[1];
        *str = (char *) malloc(len);
        err = remotecores_socket_read(p, *str, len, NULL);
        if (err) {
            return err;
        }

        /* read the trailing delimiter */
        err = remotecores_socket_read(p, tmp, 1, NULL);
        if (err) {
            return err;
        }
    }

    return REMOTECORES_SUCCESS;
}
